using System;
using System.Collections;
using System.Text;

namespace DTLAutomate
{
	public delegate void SensorTripped(int sensorId, int tagId, int trainId, double speed, bool direction, bool front);

	public class SensorDriver : IDisposable
	{
		//0x3f746c77

		private const int defaultBaudRate = 9600;
		private const string defaultComPort = "COM4";

		private bool connected;

		public bool Connected
		{
			get { return connected; }
			private set { connected = value; }
		}


		public event SensorTripped sensorTripped;

		public event NetworkMessage networkMessage;

		public static SensorDriver instance;

		private RS485Driver driver;

		private void tripSensor(int sensorId, int tagId, double speed, bool direction, bool front)
		{
			if (this.sensorTripped != null)
			{
				this.sensorTripped(sensorId, tagId, SensorDriver.decode(tagId), speed, direction, front);
			}
		}

		private static int decode(int tagId)
		{
			switch (tagId)
			{
				case 0x75767677:
					return 70;
					break;
				case 0x7576776A:
					return 30;
					break;
				case 0x757B3F7B:
					return 70;
					break;
				case 0x746C766C:
					return 40;
					break;
				case 0x7576756E:
					return 20;
					break;
				default:
					return 0;
					break;
			}
		}

		public SensorDriver(string comPort, int baudRate)
		{
			this.SensorDriver_(comPort, baudRate);
		}

		public SensorDriver(string comPort)
		{
			this.SensorDriver_(comPort, defaultBaudRate);
		}

		public SensorDriver()
		{
			this.SensorDriver_(defaultComPort, defaultBaudRate);
		}

		private void SensorDriver_(string comPort, int baudRate)
		{
			SensorDriver.instance = this;
			this.driver = new RS485Driver(comPort, baudRate);
			this.driver.appDataReady += new AppDataReady(SensorDriver.driver_appDataReady);
			this.driver.networkMessage += new NetworkMessage(driver_networkMessage);
			this.driver.start();
		}

		void driver_networkMessage(string message)
		{
			if (this.networkMessage != null) this.networkMessage(message);
		}

		static void driver_appDataReady()
		{
			byte[] packet;
			int cycles;
			int tagCode;
			int trainID = 30;
			double seconds;
			double speedMMpS;
			double speedKMpH;
			double scaledSpeedKMpH;
			double scaledSpeedMpH;


			//System.Console.Out.WriteLine("Start of driver_appDataReady()");
			if (SensorDriver.instance.driver.toApp.Count > 0)
			{
				packet = SensorDriver.instance.driver.toApp.Dequeue();
				/*System.Console.Out.WriteLine("Packet Received...");
				for (int i = 0; i < packet.Length; i++)					//WE need to change to possible packet length to 17*********************
				{
					System.Console.Out.Write("Byte {0}: 0x{1:x4}  ", i, packet[i]);
				}
				System.Console.Out.WriteLine();*/

				switch (packet[0])
				{
					case 0x00:	//Front of train
						tagCode = (packet[2] << 56) + (packet[3] << 48) + (packet[4] << 40) + (packet[5] << 32) + (packet[6] << 24) + (packet[7] << 16) + (packet[8] << 8) + (packet[9]);
						cycles = (packet[10] << 24) + (packet[11] << 16) + (packet[12] << 8) + (packet[13]);	//Get number of cycles
						seconds = Convert.ToDouble(cycles) / 15625;	//Convert cycles to time
						speedMMpS = 69f / seconds;	//Speed in mm per second
						speedKMpH = speedMMpS * 60 * 60 / (1000 * 1000);	//Speed in KM per hour
						scaledSpeedKMpH = speedMMpS * 87 * 60 * 60 / (1000 * 1000);
						scaledSpeedMpH = scaledSpeedKMpH * 0.621371;
						/*
						System.Console.Out.WriteLine("Train Sensed!!!!!");
						System.Console.Out.WriteLine("Tag ID: 0x{0:x20}", tagCode);
						System.Console.Out.WriteLine("Sensor ID: {0}", packet[1]);
						System.Console.Out.WriteLine("Time in cycles: {0}", cycles);
						System.Console.Out.WriteLine("Seconds: {0}", seconds);
						System.Console.Out.WriteLine("Direction: {0}", (packet[14] == 0) ? "Forward" : "Backward");
						System.Console.Out.WriteLine("Speed: {0} milimeters per second", speedMMpS);
						System.Console.Out.WriteLine("Speed: {0} kilometers per hour", speedKMpH);
						System.Console.Out.WriteLine("Scaled Speed: {0} kilometers per hour", scaledSpeedKMpH);
						System.Console.Out.WriteLine("Scaled Speed: {0} miles per hour", scaledSpeedMpH);
						System.Console.Out.WriteLine();
						*/
						SensorDriver.instance.tripSensor((int)packet[1], tagCode, scaledSpeedKMpH, (packet[14] == 0), true);
						break;
					case 0x01:	//Rear of train
						tagCode = (packet[2] << 56) + (packet[3] << 48) + (packet[4] << 40) + (packet[5] << 32) + (packet[6] << 24) + (packet[7] << 16) + (packet[8] << 8) + (packet[9]);
						cycles = (packet[10] << 24) + (packet[11] << 16) + (packet[12] << 8) + (packet[13]);	//Get number of cycles
						seconds = Convert.ToDouble(cycles) / 15625;	//Convert cycles to time
						speedMMpS = 69f / seconds;	//Speed in mm per second
						speedKMpH = speedMMpS * 60 * 60 / (1000 * 1000);	//Speed in KM per hour
						scaledSpeedKMpH = speedMMpS * 87 * 60 * 60 / (1000 * 1000);
						scaledSpeedMpH = scaledSpeedKMpH * 0.621371;
						/*
						System.Console.Out.WriteLine("Train Sensed!!!!!");
						System.Console.Out.WriteLine("Tag ID: 0x{0:x20}", tagCode);
						System.Console.Out.WriteLine("Sensor ID: {0}", packet[1]);
						System.Console.Out.WriteLine("Time in cycles: {0}", cycles);
						System.Console.Out.WriteLine("Seconds: {0}", seconds);
						System.Console.Out.WriteLine("Direction: {0}", (packet[14] == 0) ? "Forward" : "Backward");
						System.Console.Out.WriteLine("Speed: {0} milimeters per second", speedMMpS);
						System.Console.Out.WriteLine("Speed: {0} kilometers per hour", speedKMpH);
						System.Console.Out.WriteLine("Scaled Speed: {0} kilometers per hour", scaledSpeedKMpH);
						System.Console.Out.WriteLine("Scaled Speed: {0} miles per hour", scaledSpeedMpH);
						System.Console.Out.WriteLine("Number of Cars: {0}", packet[15]);
						System.Console.Out.WriteLine();
						*/
						SensorDriver.instance.tripSensor((int)packet[1], tagCode, scaledSpeedKMpH, (packet[14] == 0), false);
						break;
					case 0x02: //Sesnor Error
						System.Console.Error.WriteLine("Sensor Id: {0} Error Code: {1}", packet[1], packet[2]);
						System.Console.Error.WriteLine();

						break;
					case 0x40:	//Id Data Report
						//Not needed right now 

						break;
					case 0x41:	//Confirm Id data added
						//Not needed right now

						break;
					case 0x42:	//Confirm Id Data removed
						//Not needed right now

						break;
					case 0x43:	//Confirm all Id data cleared
						//Not needed right now
						break;
					case 0xC0:	//Get Id data
						//Not needed right now
						break;
					case 0xC1:	//Add Id Data
						//Not needed right now
						break;
					case 0xC2:	//Remove one Id data entry
						//Not needed right now
						break;
					case 0xC3:	//Clear all Id Data
						//Not needed right now
						break;

					default:
						break;
				}
			}
		}


		#region IDisposable Members

		public void Dispose()
		{
			if (this.driver != null) this.driver.Dispose();
			this.Connected = false;
		}

		#endregion

		internal void sendStartup()
		{
			this.driver.startup(false, null);
		}



		internal void reset()
		{
			this.driver.reset();
		}

		internal void ping(byte addr)
		{
			this.driver.ping((int)addr);
		}
	}
}
